package scuts.core.lifting;
import scuts.core.types.ProgressiveFuture;

import scuts.core.types.Option;
using scuts.core.extensions.Options;

class LiftWithProgressiveFuture1 
{

  
  public static function liftWithProgressiveFuture <A, B> (f:A->B):ProgressiveFuture<A>->ProgressiveFuture<B> 
  {
    return function (a:ProgressiveFuture<A>) {
      var fut = new ProgressiveFuture();
      a.deliverTo( function (r) fut.deliver(f(r)) );
      a.ifCanceled(function () fut.cancel());
      a.onProgress(function (p) fut.setProgress(p));
      return fut;
    }
  }
}
class LiftWithProgressiveFuture2 
{

  public static function liftWithProgressiveFuture <A, B, C> (f:A->B->C):ProgressiveFuture<A>->ProgressiveFuture<B>->ProgressiveFuture<C> 
  {
    return function (a:ProgressiveFuture<A>, b:ProgressiveFuture<B>) {
      var fut = new ProgressiveFuture();
      
      var valA = None;
      var valB = None;
      
      function check () {
        if (valA.isSome() && valB.isSome()) {
          fut.deliver(f(valA.value(), valB.value()));
        }
      }
      
      a.deliverTo(function (r) {
        valA = Some(r);
        check();
      });
      
      b.deliverTo(function (r) {
        valB = Some(r);
        check();
      });
      
      var percentA = 0.0;
      var percentB = 0.0;
      var updatePercent = function () fut.setProgress((percentA + percentB)*0.5);
      
      a.onProgress(function (p) {
        percentA = p;
        updatePercent();
      });
      
      b.onProgress(function (p) {
        percentB = p;
        updatePercent();
      });
      
      var cancel = function () fut.cancel;
      
      a.ifCanceled(cancel);
      b.ifCanceled(cancel);

      return fut;
      
    }
  }

}
class LiftWithProgressiveFuture3
{
  public static function liftWithProgressiveFuture <A, B, C, D> (f:A->B->C->D):ProgressiveFuture<A>->ProgressiveFuture<B>->ProgressiveFuture<C>->ProgressiveFuture<D>
  {
    return function (a:ProgressiveFuture<A>, b:ProgressiveFuture<B>, c:ProgressiveFuture<C>) {
      var fut = new ProgressiveFuture();
      
      var valA = None;
      var valB = None;
      var valC = None;
      
      function check () {
        if (valA.isSome() && valB.isSome() && valC.isSome()) {
          fut.deliver(f(valA.value(), valB.value(), valC.value()));
        }
      }
      
      a.deliverTo(function (r) {
        valA = Some(r);
        check();
      });
      b.deliverTo(function (r) {
        valB = Some(r);
        check();
      });
      c.deliverTo(function (r) {
        valC = Some(r);
        check();
      });
      
      var percentA = 0.0;
      var percentB = 0.0;
      var percentC = 0.0;
      var updatePercent = function () fut.setProgress((percentA + percentB + percentC)/3);
      
      a.onProgress(function (p) {
        percentA = p;
        updatePercent();
      });
      
      b.onProgress(function (p) {
        percentB = p;
        updatePercent();
      });
      c.onProgress(function (p) {
        percentC = p;
        updatePercent();
      });
      
      var cancel = function () fut.cancel;
      
      a.ifCanceled(cancel);
      b.ifCanceled(cancel);
      c.ifCanceled(cancel);
      
      return fut;
    }
  }
}

class LiftWithProgressiveFuture4 
{  
  public static function liftWithProgressiveFuture <A, B, C, D, E> (f:A->B->C->D->E):ProgressiveFuture<A>->ProgressiveFuture<B>->ProgressiveFuture<C>->ProgressiveFuture<D>->ProgressiveFuture<E>
  {
    return function (a:ProgressiveFuture<A>, b:ProgressiveFuture<B>, c:ProgressiveFuture<C>, d:ProgressiveFuture<D>) {
      var fut = new ProgressiveFuture();
      
      var valA = None;
      var valB = None;
      var valC = None;
      var valD = None;
      
      function check () {
        if (valA.isSome() && valB.isSome() && valC.isSome() && valD.isSome()) {
          fut.deliver(f(valA.value(), valB.value(), valC.value(), valD.value()));
        }
      }
      
      a.deliverTo(function (r) {
        valA = Some(r);
        check();
      });
      b.deliverTo(function (r) {
        valB = Some(r);
        check();
      });
      c.deliverTo(function (r) {
        valC = Some(r);
        check();
      });
      d.deliverTo(function (r) {
        valD = Some(r);
        check();
      });
      
      var percentA = 0.0;
      var percentB = 0.0;
      var percentC = 0.0;
      var percentD = 0.0;
      var updatePercent = function () fut.setProgress((percentA + percentB + percentC + percentD)*0.25);
      
      a.onProgress(function (p) {
        percentA = p;
        updatePercent();
      });
      
      b.onProgress(function (p) {
        percentB = p;
        updatePercent();
      });
      c.onProgress(function (p) {
        percentC = p;
        updatePercent();
      });
      d.onProgress(function (p) {
        percentD = p;
        updatePercent();
      });
      
      var cancel = function () fut.cancel;
      
      a.ifCanceled(cancel);
      b.ifCanceled(cancel);
      c.ifCanceled(cancel);
      d.ifCanceled(cancel);
      
      return fut;
    }
  }
  
}